package com.gitee.wsl.model

import com.gitee.wsl.model.plugin.ModelPlugin
import com.gitee.wsl.model.plugin.PLUGIN_INSTALLED_LIST
import com.gitee.wsl.prop.AttributeKey
import com.gitee.wsl.prop.Attributes
import kotlin.collections.plusAssign

open class ModelConfig<TModel:Model<TModel>> {

    val plugins: MutableMap<AttributeKey<*>, (TModel) -> Unit> = mutableMapOf()
    val pluginConfigurations: MutableMap<AttributeKey<*>, Any.() -> Unit> = mutableMapOf()
    val customInterceptors: MutableMap<String, (TModel) -> Unit> = mutableMapOf()

    /**
     * Installs the specified [plugin] and optionally configures it using the [configure] block.
     * Learn more from [Plugins](https://ktor.io/docs/http-client-plugins.html).
     */
     fun <TBuilder, TPlugin> install(
        plugin: ModelPlugin<TModel, TBuilder, TPlugin>,
        configure: TBuilder.() -> Unit = {}
    ) {
        val previousConfigBlock = pluginConfigurations[plugin.key]
        pluginConfigurations[plugin.key] = {
            previousConfigBlock?.invoke(this)

            @Suppress("UNCHECKED_CAST")
            (this as TBuilder).configure()
        }

        if (plugins.containsKey(plugin.key)) return

        plugins[plugin.key] = { scope ->
            val attributes = scope.attributes.computeIfAbsent(PLUGIN_INSTALLED_LIST) { Attributes(concurrent = true) }
            val config = scope.config.pluginConfigurations[plugin.key]!! as TBuilder.() -> Unit
            val pluginData = plugin.prepare(config)

            plugin.install(pluginData, scope)
            attributes.put(plugin.key, pluginData)
        }
    }

    /**
     * Installs an interceptor defined by [block].
     * The [key] parameter is used as a unique name, that also prevents installing duplicated interceptors.
     */
    fun install(key: String, block: TModel.() -> Unit) {
        customInterceptors[key] = block
    }

    /**
     * Applies all the installed [plugins] and [customInterceptors] from this configuration
     * into the specified [model].
     */
    fun install(model: TModel) {
        plugins.values.forEach { model.apply(it) }
        customInterceptors.values.forEach { model.apply(it) }
    }

    /**
     * Clones this [HttpClientConfig] by duplicating all the [plugins] and [customInterceptors].
     */
    open fun clone(): ModelConfig<TModel> {
        val result = ModelConfig<TModel>()
        result += this
        return result
    }

    /**
     * Installs the plugin from the [other] client's configuration.
     */
     operator fun plusAssign(other: ModelConfig<TModel>) {
        //followRedirects = other.followRedirects
        //useDefaultTransformers = other.useDefaultTransformers
        //expectSuccess = other.expectSuccess

        this.plugins += other.plugins
        pluginConfigurations += other.pluginConfigurations
        customInterceptors += other.customInterceptors
    }
}